﻿using System;
using System.Data;
using System.Collections;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Data.SqlClient;

/// <summary>
/// CAnalyzeAction 어휘 분석
/// </summary>
public class CAnalyzeAction
{
    CDatabase _db = new CDatabase();

    public String text;     // 지문
    public String levelId;  // 시리즈

    // 난이도 지수
    public double[] levelBase = {1.0, 1.2, 1.4, 2.0, 2.2, 2.4, 3.0, 2.0};                   
    // 제거 대상
    public String[] toBlankChar = {"\r", "\n", "\t", "\"", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", 
                                   "=", "{", "}", "[", "]", "|", "\\", ":", ";", ",", "/", "<", ">", "“", "”", "…" };
    // 문장 단위 기호
    public String[] toDotChar = { "?", "!" };

    public ArrayList sentenceList;          // 문장 배열
    public int[] sentenceLengths;           // 문장당 단어수 배열
    public ArrayList wordList;              // 단어 리스트
    public ArrayList compoundList;          // 혼합어
    
    public CGuideLine guideLine;            // 선택 시리즈의 guide line 데이터
    
    public int sentenceCnt = 0;             // 문장 개수
    public int compoundCnt = 0;             // 혼합어 개수
    public int overCnt = 0;                 // 단어 총 개수(중복 포함)
    public int nonOverCnt = 0;              // 단어 총 개수(중복 제외)

    public Hashtable overloadWord;          // 레벨별 어휘 분포도 (중복 포함)
    public Hashtable nonOverLoadWord;       // 레벨별 어휘 분포도 (중복 제외)
    public Hashtable sortWordTable;         // sort용 단어들

    public ArrayList levelSortList;         // 레벨 sort    (레벌 오름, 빈도 내림)
    public ArrayList freqSortList;          // 빈도 sort    (빈도 내림, 레벨 오름)
    public ArrayList alphaSortList;         // 알파벳 sort  (알파벳 오름, 레벨 오름)

    private int round = 2;

    private Hashtable wordTable = null;
    private Hashtable variationWordTable = null;

    // Constructor
    public CAnalyzeAction(String text, String levelId) {
        this.text = text;
        this.levelId = levelId;
        init();
    }

    private void init()
    {
        try
        {
            sentenceCnt = 0;             // 문장 개수
            compoundCnt = 0;             // 혼합어 개수
            overCnt = 0;                 // 단어 총 개수(중복 포함)
            nonOverCnt = 0;

            _db.GetConnection();

            doParse();

            setGuidLineData();
            checkWord();

            checkGuideLine();
            checkDistribution();

            sort();
        }
        catch (Exception e)
        {

        }
        finally
        {
            _db.ReleaseConnection();
        }
    }

    /*
     * 지문 분석 -> 파서
     */
    public void doParse()
    {
        // 축약형 수정
        replaceAcronymWord();

        //1. 특수 문자 제거
        replaceBlank();
        //2. 문장 부호 변경
        replaceDot();
        //3. 문장 분리
        splitSentence();

        //4. 단어 분리
        splitWord();
    }

    // 축약형 단어
    private void replaceAcronymWord() { 
        this.text = this.text.Replace("a.m.", "a1m1");
        this.text = this.text.Replace("p.m.", "p1m1");
    }

    private String replaceApostropheWord(String word)
    {
        string[] replaceApo = { "'s", "'ll", "'d", "'re", "n't", "'ve", "'t", "'m"};
        word = word.ToLower();

        if(word.Contains("'")){

            word = word.Replace("can't", "can");
            word = word.Replace("won't", "will");
            word = word.Replace("ain't", "am");

            for (int i = 0; i < replaceApo.Length; i++) {
                String pattern = "\\w+" + replaceApo[i];
                if (Regex.IsMatch(word, pattern))
                {
                    word = word.Replace(replaceApo[i], "");
                    break;
                }
            }
        }

        word = word.Replace("'", "");
        word = word.Replace("’", "");

        return word;
    }

    private void replaceBlank()
    {
        this.text = text.Replace("’", "\'");
        for (int i = 0; i < this.toBlankChar.Length; i++)
        {
            this.text = this.text.Replace(toBlankChar[i], " ");
        }
    }

    private void replaceDot()
    {
        for (int i = 0; i < this.toDotChar.Length; i++)
        {
            this.text = this.text.Replace(toDotChar[i], ".");
        }
    }

    // 문장 분리
    private void splitSentence()
    {
        String[] tempSList = this.text.Split('.');
        sentenceList = new ArrayList();

        for (int i = 0; i < tempSList.Length; i++)
        {
            tempSList[i] = tempSList[i].Trim();
            if (tempSList[i] != "")
            {
                sentenceList.Add(tempSList[i]);
            }
        }
        sentenceCnt = sentenceList.Count;
        sentenceLengths = new int[sentenceCnt];
    }

    /* 단어 분리
     *  1. compound word 는 1개의 단어로 카운트.
     *  2. can't -> can으로 인식 한 단어.
     */
    private void splitWord()
    {
        setCompoundWord();              // 혼합어

        wordTable = CWordList.wordList;
        variationWordTable = CWordList.variationWordList;

        wordList = new ArrayList();

        for (int i = 0; i < sentenceList.Count; i++)
        {
            int s = 0;  // 문장별 단어 개수

            String snt = (String)sentenceList[i];
            if (snt != "")
            {
                // 혼합어
                for (int j = 0; j < compoundList.Count; j++)
                {
                    CWord w = (CWord)compoundList[j];
                    String compareString = isBlank(w.variationWord) ? w.headWord : w.variationWord;
                    if (snt.Contains(compareString))
                    {
                        compoundCnt++;
                        s++;
                        wordList.Add(w);
                        snt = snt.Remove(snt.IndexOf(compareString), compareString.Length);
                        j--;
                    }
                }
            }

            // 단어 분리

            // 축약형 원상복구
            snt = snt.Replace("a1m1", "a.m.");
            snt = snt.Replace("p1m1", "p.m.");

            String[] words = snt.Split(' ');

            for (int j = 0; j < words.Length; j++)
            {
                String wordData = replaceApostropheWord(words[j]);

                if (isBlank(wordData))
                {
                    continue;
                }

                s++;

                
                CWord w = new CWord();

                /*
                //Head word
                string sql = "SELECT word, wordlevel, '' as wordvariation, 'head' as type FROM ANALYZE_WORD_NEW WHERE word = '" + wordData + "'";
                SqlDataReader dr = _db.GetQueryResult(sql);

                //Variation word
                if (!dr.HasRows)
                {
                    dr.Close();
                    sql = "SELECT word, wordlevel, wordvariation, 'variation' as type FROM ANALYZE_WORD_VARIATION_NEW WHERE WordVariation ='" + wordData + "'";
                    dr = _db.GetQueryResult(sql);
                }

                // word setting
                if (dr.HasRows)
                {
                    dr.Read();
                    w.headWord = dr["word"].ToString();
                    w.variationWord = dr["wordvariation"].ToString();
                    w.level = int.Parse(dr["wordlevel"].ToString());
                    w.type = dr["type"].ToString();
                }
                else
                {
                    w.headWord = wordData;
                    w.variationWord = "";
                    w.type = "head";
                }
                dr.Close();

                // view word (화면에 보이지는 단어)
                w.viewWord = isBlank(w.variationWord) ? w.headWord : w.variationWord;
                */


                if (wordTable.ContainsKey(wordData.ToLower()))
                {
                    w = ((CWord)wordTable[wordData]);
                }
                else if (variationWordTable.ContainsKey(wordData.ToLower()))
                {
                    w = (CWord)variationWordTable[wordData];
                }
                else {
                    w.headWord = wordData;
                    w.variationWord = "";
                    w.type = "head";
                }
                w.frequence = 0;
                w.viewWord = isBlank(w.variationWord) ? w.headWord : w.variationWord;

                wordList.Add(w);
            }

            // 문장 별 단어 개수.
            sentenceLengths[i] = s;
        }
    }

    private void setCompoundWord()
    {
        compoundList = new ArrayList();
        string sql = "Select * from (select word, wordlevel, '' as wordvariation, 'head' as type from ANALYZE_WORD_NEW where Wordtype = 'CP' union all select word, wordlevel, wordvariation, 'variation' as type from ANALYZE_WORD_VARIATION_NEW where Wordtype = 'CP') A order by wordvariation desc, word desc ";
        SqlDataReader dr = _db.GetQueryResult(sql);

        if (dr.HasRows)
        {
            while (dr.Read())
            {
                CWord w = new CWord();
                w.isCompount = true;
                w.headWord = dr["word"].ToString();
                w.variationWord = dr["wordvariation"].ToString();
                w.level = int.Parse(dr["wordlevel"].ToString());
                w.type = dr["type"].ToString();
                w.viewWord = isBlank(w.variationWord) ? w.headWord : w.variationWord;
                compoundList.Add(w);
            }
        }
        dr.Close();
    }

    private void setGuidLineData(){
        guideLine = new CGuideLine();

        if (levelId != "")
        {
            String sql = "Select LevelId,StLenMin, StLenMax, AvgLenMin, AvgLenMax, StLevelMin, StLevelMax, DisScope1, DisMin1, DisMax1, DisScope2, DisMin2, DisMax2, DisScope3, DisMin3, DisMax3, ModDate, ModUser From  ANALYZE_WORD_GUIDELINE Where LevelId = '" + levelId + "'";
            SqlDataReader dr = _db.GetQueryResult(sql);

            if (dr.HasRows)
            {
                dr.Read();
                guideLine.levelId = dr["LevelId"].ToString();
                guideLine.stLenMin = dr["StLenMin"].ToString();
                guideLine.stLenMax = dr["StLenMax"].ToString();
                guideLine.avgLenMin = dr["AvgLenMin"].ToString();
                guideLine.avgLenMax = dr["AvgLenMax"].ToString();
                guideLine.stLevelMin = dr["StLevelMin"].ToString();
                guideLine.stLevelMax = dr["StLevelMax"].ToString();
                guideLine.disScope1 = dr["DisScope1"].ToString();
                guideLine.disMin1 = dr["DisMin1"].ToString();
                guideLine.disMax1 = dr["DisMax1"].ToString();
                guideLine.disScope2 = dr["DisScope2"].ToString();
                guideLine.disMin2 = dr["DisMin2"].ToString();
                guideLine.disMax2 = dr["DisMax2"].ToString();
                guideLine.disScope3 = dr["DisScope3"].ToString();
                guideLine.disMin3 = dr["DisMin3"].ToString();
                guideLine.disMax3 = dr["DisMax3"].ToString();
            }
            dr.Close();
        }
    }

    private void checkWord()
    {
        initOverWordTable();
        Hashtable tmpWordTable = new Hashtable();
        sortWordTable = new Hashtable();

        for (int i = 0; i < wordList.Count; i++) {
            CWord wd = (CWord)wordList[i];

            Distribution ds = new Distribution();

            int c = 0;
            if (overloadWord.Contains(wd.level))
            {
                c = ((Distribution)overloadWord[wd.level]).cnt;
            }
            overloadWord.Remove(wd.level);
            ds.word = wd.headWord;
            ds.cnt = c + 1;
            overloadWord.Add(wd.level, ds);

            int d = 0;
            if (!tmpWordTable.ContainsKey(wd.headWord))
            {
                tmpWordTable.Add(wd.headWord, wd.level);

                Distribution ds2 = new Distribution();
                if (nonOverLoadWord.Contains(wd.level))
                {
                    d = ((Distribution)nonOverLoadWord[wd.level]).cnt;
                }
                nonOverLoadWord.Remove(wd.level);
                ds2.word = wd.headWord;
                ds2.cnt = d + 1;

                nonOverLoadWord.Add(wd.level, ds2);
                nonOverCnt++;
            }
            overCnt++;

            // Sort
            CWord temp = new CWord();
            if (sortWordTable.ContainsKey(wd.headWord))
            {
                temp = (CWord)sortWordTable[wd.headWord];
            }
            else
            {
                temp.headWord = wd.headWord;
                temp.variationWord = wd.variationWord;
                temp.viewWord = wd.viewWord;
                temp.level = wd.level;
            }
            temp.frequence++;

            // 단어 리스트
            Boolean isIn = false;
            ArrayList vList = temp.wordList;
            for (int j = 0; j < vList.Count; j++)
            {
                CWord vword = (CWord)vList[j];
                if (vword.viewWord == wd.viewWord)
                {
                    isIn = true;       
                    vword.frequence++;
                    temp.wordList.RemoveAt(j);
                    temp.wordList.Add(vword);
                    break;
                }
            }
            if (!isIn)
            {
                wd.frequence++;
                temp.wordList.Add(wd);
            }

            temp.wordList.Sort(new SortHelper1());
            sortWordTable.Remove(wd.headWord);
            sortWordTable.Add(wd.headWord, temp);
        }
    }

    private void initOverWordTable()
    {
        overloadWord = new Hashtable();
        nonOverLoadWord = new Hashtable();

        for (int i = 1; i <= 8; i++) {
            overloadWord.Add(i, new Distribution());
            nonOverLoadWord.Add(i, new Distribution());
        }
    }

    private void checkGuideLine()
    {
        // 지문 길이
        guideLine.len = overCnt.ToString();
        guideLine.isLen = checkIn((double)overCnt, guideLine.stLenMin, guideLine.stLenMax);

        // 평균 문장 길이
        double temp = overCnt / (double)sentenceCnt;
        guideLine.avg = Math.Round(temp, round).ToString();
        guideLine.isAvg = checkIn(temp, guideLine.avgLenMin, guideLine.avgLenMax);

        //텍스트 난이도 지수
        IDictionaryEnumerator en = overloadWord.GetEnumerator();

        double l = 0.0;
        while (en.MoveNext())
        {
            Distribution ds = (Distribution)en.Value;
            int level = (int)en.Key;
            l += (ds.cnt / (double)overCnt * levelBase[level - 1]);
        }
        l = l * temp;
        guideLine.lv = Math.Round(l, round).ToString();
    }

    private void checkDistribution(){
        IDictionaryEnumerator en = overloadWord.GetEnumerator();
        while (en.MoveNext())
        {
            Distribution ds = (Distribution)en.Value;
            ds.percent = Math.Round((ds.cnt / (double)overCnt) * 100.0, round);
        }

        en = nonOverLoadWord.GetEnumerator();
        while (en.MoveNext())
        {
            Distribution ds = (Distribution)en.Value;
            ds.percent = Math.Round((ds.cnt / (double)nonOverCnt) * 100.0, round);
        }
    }

    private void sort() { 
        levelSortList = new ArrayList();    // 레벨d 빈도u
        freqSortList = new ArrayList();     // 빈도u 레벨d
        alphaSortList = new ArrayList();    // 알파벳d 레벨d

        IDictionaryEnumerator en = sortWordTable.GetEnumerator();
        while (en.MoveNext())
        {
            CWord tData = (CWord)en.Value;
            levelSortList.Add(tData);
            freqSortList.Add(tData);
            alphaSortList.Add(tData);
        }
        levelSortList.Sort(new SortHelper1());
        freqSortList.Sort(new SortHelper2());
        alphaSortList.Sort(new SortHelper3());
    }

    private Boolean checkIn(double cnt, String min, String max)
    {
        Boolean cin = true;
        if (!isBlank(min) && !isBlank(max))
        {
            double dMin = double.Parse(min);
            double dMax = double.Parse(max);
            cin = (cnt <= dMax && cnt >= dMin);
        }
        return cin;
    }

    public Boolean isBlank(String str) {
        Boolean isnull = true;

        if (str != null) {
            if (str.Trim() != "") {
                isnull = false;
            }
        }

        return isnull;
    }

    class SortHelper1 : IComparer
    {
        int IComparer.Compare(object x, object y) {
            int result = 0;

            if (x == null || y == null) {
                throw new Exception("compare 1 error..");
            }

            CWord c1 = x as CWord;
            CWord c2 = y as CWord;

            result = c1.level - c2.level;
            if (result == 0) {
                result = c2.frequence - c1.frequence;
            }

            return result;
        }
    }

    class SortHelper2 : IComparer
    {
        int IComparer.Compare(object x, object y)
        {
            int result = 0;

            if (x == null || y == null)
            {
                throw new Exception("compare 1 error..");
            }

            CWord c1 = x as CWord;
            CWord c2 = y as CWord;

            result = c2.frequence - c1.frequence; 
            if (result == 0)
            {
                result = c1.level - c2.level;
            }

            return result;
        }
    }

    class SortHelper3 : IComparer
    {
        int IComparer.Compare(object x, object y)
        {
            int result = 0;

            if (x == null || y == null)
            {
                throw new Exception("compare 1 error..");
            }

            CWord c1 = x as CWord;
            CWord c2 = y as CWord;

            String w1 = isBlank2(c1.variationWord) ? c1.headWord : c1.variationWord;
            String w2 = isBlank2(c2.variationWord) ? c2.headWord : c2.variationWord;

            result = w1.CompareTo(w2);

            if (result == 0)
            {
                result = c1.level - c2.level;
            }

            return result;
        }

        private Boolean isBlank2(String str)
        {
            Boolean isnull = true;

            if (str != null)
            {
                if (str.Trim() != "")
                {
                    isnull = false;
                }
            }

            return isnull;
        }
    }
}
